3. Certificates
As discussed previously,
SQL Azure doesn't support X.509 certificates, although you can deploy
X.509 certificates in Windows Azure. Your client code (either hosted on
your company's network or in Windows Azure) can use certificates to
encrypt and decrypt values. The use of certificates implies that you're
encrypting using a public/private key pair. The public key is used to
encrypt data, and the private key is used to decrypt data.
NOTE
For more information on how to deploy X.509 certificates in Windows Azure, visit the MSDN blog http://blogs.msdn.com/jnak and look at the January 2010 archive. The blog entry by Jim Nakashima contains detailed instructions.
You can easily create a self-signed certificate using the MakeCert.exe
utility. To create a certificate on your machines, run the following
command at a command line. You need to execute this statement as an
Administrator or the command will fail:
makecert -ss root -pe -r -n "CN=BlueSyntaxTest" -sky Exchange -sr LocalMachine
Here is a brief overview of the options used to create this certificate:
-ss root stores the certificate in the root certificate store.
-pe marks the private key exportable.
-r creates a self-signed certificate (meaning that it wasn't issued by a root certificate authority (CA) like Thawte).
-n "CN=..." specifies the subject's name of the certificate.
-sky Exchange specifies that the certificate is used for encryption.
-sr LocalMachine specifies that the certificate store location as LocalMachine.
NOTE
Make sure you run this statement as an Administrator, or you'll get an error that looks like this: Error:Save encoded certificate to store failed => 0x5 (5).
To verify that your certificate was properly installed, open mmc.exe.
Select File→ Add/Remove Snap In. Then, select Certificates, click Add,
choose Computer, and click OK. Expand the tree on the left to view the
certificates under Trusted Root Certification Authorities. Figure 3 shows the BlueSyntaxTest certificate that was created with the earlier command.
Now that you have a certificate installed, you can search for and locate
it with code. Usually, a certificate is searched for by its unique
identifier (thumbprint) or by its common name
(CN). To view the thumbprint of your certificate, double-click the
certificate, select the Details tab, and scroll down until you see the Thumbprint property, as shown in Figure 4.
You can select the
thumbprint and copy it into a string variable. The following code shows a
new private variable and a new method in the Encryption class you saw earlier. Line 1 contains the thumbprint as seen in Figure 4, line 13 opens the root certificate store on LocalMachine, and line 17 instantiates an X.509 object by searching the thumbprint. Note that the Find
method returns a collection; you're interested in the first certificate
because only one will match this thumbprint. On line 24, you create the
RSA encryption object and call its Encrypt
method on line 27. Because encrypting with RSA automatically
incorporates a vector, there is no need to keep track of it. So, the CipherText vector variable is set to 0:
1. private string _THUMBPRINT_ =
2. "01 71 11 17 0a b4 96 7b ca 1f f3 e5 bc 0f 68 9d c6 c0 3b 7b";
3.
4. /// <summary>
5. /// Encrypts a string value using a self-signed certificate
6. /// </summary>
7. /// <param name="value">The value to encrypt</param>
8. /// <returns></returns>
9. public CipherText EncryptByCert(string value)
10. {
11. byte[] buffer = UTF8Encoding.UTF8.GetBytes(value);
12.
13. X509Store store = new X509Store(StoreName.Root,
14. StoreLocation.LocalMachine);
15. store.Open(OpenFlags.ReadOnly);
16.
17. X509Certificate2 x509 =
18. store.Certificates.Find(
19. X509FindType.FindByThumbprint,
20. _THUMBPRINT_, true)[0];
21.
22. store.Close();
23.
24. RSACryptoServiceProvider rsaEncrypt = null;
25. rsaEncrypt = (RSACryptoServiceProvider)x509.PublicKey.Key;
26.
27. byte[] encryptedBytes = rsaEncrypt.Encrypt(buffer, false);
28.
29. CipherText ct = new CipherText();
30. ct.cipher = encryptedBytes;
31. ct.vector = new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0,
32. 0, 0, 0, 0, 0, 0, 0};
33.
34. return ct;
35. }
The decryption code is shown next and is very similar to the preceding example. You make a call to Decrypt instead of Encrypt on the RSA object:
1. public string DecryptByCert(CipherText ct)
2. {
3. X509Store store = new X509Store(StoreName.Root,
4. StoreLocation.LocalMachine);
5. store.Open(OpenFlags.ReadOnly);
6.
7. X509Certificate2 x509 =
8. store.Certificates.Find(
9. X509FindType.FindByThumbprint,
10. _THUMBPRINT_, true)[0];
11. store.Close();
12.
13. RSACryptoServiceProvider rsaEncrypt = null;
14. rsaEncrypt = (RSACryptoServiceProvider)x509.PrivateKey;
15.
16. byte[] bytes = rsaEncrypt.Decrypt(ct.cipher, false);
17.
18. return UTF8Encoding.UTF8.GetString(bytes);
19. }
The following code calls the
RSA encryption routine and saves to the UserProperties table as
previously described. The table now contains two records. Note that the
length of the ciphertext is much greater with the certificate encryption
approach:
1. class Program
2. {
3. static void Main(string[] args)
4. {
5. // Declare the encryption object and encrypt our secret value
6. Encryption e = new Encryption();
7. CipherText ct = e.EncryptAES("secret value goes here...");
8. CipherText ct2 = e.EncryptByCert("another secret!!!");
9.
10. UserProperties.Save("MySecret", ct);
11. UserProperties.Save("MySecret2", ct2);
12.
13. }
14. }